package com.forusers.escrevecheque.client;

/**
 * Convert a decimal number into a cardinal text representation of that number.
 * Ex. 123, "cento e vinte tres".
 * 
 * @author scott
 */
public class CardinalNumber {
	static class CNumInfo {
		private int num;
		private String masc;
		private String fem;

		public CNumInfo(int num, String masc, String fem) {
			this.num = num;
			this.masc = masc;
			this.fem = fem;
		}
	};
	private static final CNumInfo PORT[] = {  
		new CNumInfo(  0, "zero",        "zero"),
		new CNumInfo(  1, "um",          "uma"),
		new CNumInfo(  2, "dois",        "duas"),
		new CNumInfo(  3, "três",        "três"),
		new CNumInfo(  4, "quatro",      "quatro"),
		new CNumInfo(  5, "cinco",       "cinco"),
		new CNumInfo(  6, "seis",        "seis"),
		new CNumInfo(  7, "sete",        "sete"),
		new CNumInfo(  8, "oito",        "oito"),
		new CNumInfo(  9, "nove",        "nove"),
		new CNumInfo( 10, "dez",         "dez"),
		new CNumInfo( 11, "onze",        "onze"),
		new CNumInfo( 12, "doze",        "doze"),
		new CNumInfo( 13, "treze",       "treze"),
		new CNumInfo( 14, "quatorze",    "quatorze"),
		new CNumInfo( 15, "quinze",      "quinze"),
		new CNumInfo( 16, "dezesseis",   "dezesseis"),
		new CNumInfo( 17, "dezessete",   "dezessete"),
		new CNumInfo( 18, "dezoito",     "dezoito"),
		new CNumInfo( 19, "dezenove",    "dezenove"),
		new CNumInfo( 20, "vinte",       "vinte"),
		new CNumInfo( 30, "trinta",      "trinta"),
		new CNumInfo( 40, "quarenta",    "quarenta"),
		new CNumInfo( 50, "cinquenta",   "cinquenta"),
		new CNumInfo( 60, "sessenta",    "sessenta"),
		new CNumInfo( 70, "setenta",     "setenta"),
		new CNumInfo( 80, "oitenta",     "oitenta"),
		new CNumInfo( 90, "noventa",     "noventa"),
		new CNumInfo(100, "cento",       "cem"),
		new CNumInfo(200, "duzentos",    "duzentas"),
		new CNumInfo(300, "trezentos",   "trezentas"),
		new CNumInfo(400, "quatrocentos","quatrocentos"),
		new CNumInfo(500, "quinhentos",  "quinhentas"),
		new CNumInfo(600, "seiscentos",  "seiscentas"),
		new CNumInfo(700, "setecentos",  "setecentas"),
		new CNumInfo(800, "oitocentos",  "oitocentas"),
		new CNumInfo(900, "novecentos",  "novecentas"),
		new CNumInfo(1000,"mil",         "mil"),
		new CNumInfo(1000000, "milhão",  "milhões")};
	private static final String[] MINUS_PLUS = {
		"menos", "mais",  // Portuguese
	};
	private static final String AND = " e ";

	String getNumberOnly(int num) {
		return getNumberOnly(num, true);
	}
	
	/**
	 * Return the number in string representation.
	 * 
	 * @param num The number to convert into a a cardinal string.  Can be negative.
	 * @param masculine Whether the object is masculine or feminine. 
	 * @return
	 */
	public String getNumberOnly(int num, boolean masculine) {
		StringBuffer answer =  new StringBuffer();
		final int maxNumbers = PORT.length;
		final int n100 = 28;
		final int n900 = 36;

		if (num < 0) {
			answer.append(MINUS_PLUS[0]);
			answer.append(" ");
			num = -num;
		}

		if (num == 0) {
			// Special case, number is zero
			answer.append(masculine ? PORT[0].masc : PORT[0].fem);
		}
		else if (num == 100) {
			// Special case, number is exactly 100
			answer.append(PORT[n100].fem);
		}
		else {
			int work = num;

			// Numbers greater than a 1000 don't get the "e " between parts
			while (work >= 1000) {
				int index;
				for (index = maxNumbers - 1; index > n900; --index) {
					if (PORT[index].num <= work) {
						int closestNum = PORT[index].num;

						int subAmount = work;
						subAmount /= closestNum;
						work -= subAmount * closestNum;

						answer.append(getNumberOnly(subAmount, masculine));
						answer.append(" ");

						answer.append(PORT[index].masc);
						if (work > 0) {
							answer.append(" ");
						}

						break;
					}
				}
				
				if (n900 == index) {
					break;
				}
			}

			while (work > 0) {
				for (int nIndex = n900; nIndex > 0; --nIndex) {
					if (PORT[nIndex].num <= work) {
						answer.append(masculine ? PORT[nIndex].masc : PORT[nIndex].fem);

						work -= PORT[nIndex].num;
						if (work > 0) {
							answer.append(AND);
						}
						break;
					}
				}
			}
		}

		return answer.toString();
	}
}
